﻿using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static light.http.server.httpserver;

namespace server06_httpsandwss
{
    class Program
    {
        /// <summary>
        /// https 需要一個 pfx 格式的證書
        /// 可以用openssl生成
        /// 1.安裝openssl略

        /// 2.執行
        /// openssl req -x509 -out localhost.cer -keyout localhost.key \
        /// -newkey rsa:2048 -nodes -sha256 \
        /// -subj '/CN=localhost' -extensions EXT -config<( \
        /// printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

        /// 此時可得到localhost.key 和 localhost.cer 兩個文件，這還不是pfx格式，需要封裝

        /// 3.執行
        /// openssl pkcs12 -export -in localhost.cer -inkey localhost.key -out localhost.pfx
        /// 過程中需要輸入密碼

        /// 此時可得到localhost.pfx文件
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //本例中pfx文件 密碼為123456，使用上述注釋方法獲取
            //注意這個證書的根證書并不被瀏覽器認可，會有警告，正式使用的證書需注冊，同IIS
            //需要先將這個cer文件導入到受信任的根證書頒發機構

            var server = new light.http.server.httpserver();
            server.SetFailAction(on404);
            server.SetHttpAction("/abc", onAbc);
            //這樣就可以開啓https，
            server.SetWebsocketAction("/ws", onWebSocketConnect);
            server.Start(1988,1989,"localhost.pfx","123456");
            Console.WriteLine("server on:1988 http   try it:http://localhost:1988/abc");
            Console.WriteLine("server on:1989 https   try it:https://localhost:1989/abc");
            Console.WriteLine("server on:1988 websocket   try it:ws://localhost:1988/ws");
            Console.WriteLine("server on:1989 websocket(serect)   try it:wss://localhost:1989/ws");

            while (true)
            {
                Console.ReadLine();
            }
        }
        static IWebSocketPeer onWebSocketConnect(System.Net.WebSockets.WebSocket websocket)
        {
            return new MyPeer(websocket);
        }
        static async Task on404(HttpContext context)
        {
            await context.Response.WriteAsync("you got a 404");
        }
        static async Task onAbc(HttpContext context)
        {
            await context.Response.WriteAsync("you got a abc");

        }
    }
    class MyPeer : IWebSocketPeer
    {
        System.Net.WebSockets.WebSocket websocket;
        public MyPeer(System.Net.WebSockets.WebSocket websocket)
        {
            this.websocket = websocket;
        }
        //接口实现 连接接通回调 
        public async Task OnConnect()
        {
            Console.WriteLine("OnConnect.");
            return;
        }
        //接口实现 连接断开回调
        public async Task OnDisConnect()
        {
            Console.WriteLine("OnDisConnect.");
            return;
        }

        //接口实现 收到信息，websocket 自带分包处理，底层处理了拼包，每次OnRecv一定是一个完整的包
        public async Task OnRecv(MemoryStream stream, int count)
        {
            byte[] buf = new byte[count];
            stream.Read(buf, 0, buf.Length);
            var str = System.Text.Encoding.UTF8.GetString(buf);
            Console.WriteLine("recv:" + str);

            var outbuf = System.Text.Encoding.UTF8.GetBytes("hello world.");
            await SendAsync(outbuf);
        }
        //发送方法
        private async Task SendAsync(byte[] msg)
        {
            await this.websocket.SendAsync(
            msg,//发包 类型为ArraySegment<byte> byte[]的一部分，直接传byte[]会隐式自动转换
            System.Net.WebSockets.WebSocketMessageType.Text,//包类型 二进制还是Text
            //大部分情况无所谓，但js要注意，ws in js，文本会直接收到字符串，不能发非字符，二进制会收到一个blob对象
            true,//是否是结束包，写逻辑没必要专门把一个包分几次发，基本上会一直取true
            CancellationToken.None//Task取消机制，略
            );
        }
    }

}
